<?php

/**
 * Class Docs
 * @ignore
 */
class DocsController extends CI_Controller
{
    /**
     * ApiList - 在线接口列表文档
     * @desc 查看所有控制器/方法列表
     */
    public function index()
    {
        $allApiS = array();

        // 控制器目录
        $ctlPath = __DIR__;

        // 扫描接口文件
        $files = $this->__listDir($ctlPath);
        foreach ($files as $aFile) {

            if (strpos($aFile, '.php') === FALSE) {
                continue;
            }

            include_once $aFile;

            $dirName = str_replace($ctlPath, '', dirname($aFile));// 获取目录名
            $fileName = basename($aFile, '.php');
            $className = $fileName . 'Controller'; // 获取类名

            // 左侧菜单的标题
            $ref = new \ReflectionClass($className);
            $isClassIgnore = false; // 是否屏蔽此接口类
            $docComment = $ref->getDocComment();
            if ($docComment !== false) {
                $docCommentArr = explode("\n", $docComment);
                foreach ($docCommentArr as $comment) {
                    if (stripos($comment, '@ignore') !== false) {
                        $isClassIgnore = true;
                    }
                }
            }

            if ($isClassIgnore) {
                continue;
            }
            // 获取所有方法
            $methodName = get_class_methods($className);
            $methodName = array_diff($methodName, ['get_instance', 'getRules']);

            foreach ($methodName as $mValue) {
                $rMethod = new \Reflectionmethod($className, $mValue);
                if (!$rMethod->isPublic() || strpos($mValue, '_') === 0) {
                    continue;
                }

                $title = '//请检测函数注释';
                $desc = '//请使用@desc 注释';
                $isMethodIgnore = false;
                $docComment = $rMethod->getDocComment();
                if ($docComment !== false) {
                    $docCommentArr = explode("\n", $docComment);
                    $comment = trim($docCommentArr[1]);
                    $title = trim(substr($comment, strpos($comment, '*') + 1));

                    foreach ($docCommentArr as $comment) {
                        $pos = stripos($comment, '@desc');
                        if ($pos !== false) {
                            $desc = trim(substr($comment, $pos + 5));
                        }

                        if (stripos($comment, '@ignore') !== false) {
                            $isMethodIgnore = true;
                        }
                    }
                }

                if ($isMethodIgnore) {
                    continue;
                }

                if ($dirName !== "") {
                    $dirName = trim($dirName, '/') . "/";
                }

                $service = $dirName . lcfirst($fileName) . '/' . $mValue;
                $allApiS[$service] = array(
                    'service' => $service,
                    'title' => $title,
                    'desc' => $desc,
                );
            }

        }

        ksort($allApiS);

        // 按照字母排序，如无此需求可直接return $allApiS
        $list = [];
        foreach ($allApiS as $key => $val) {
            if (!in_array(substr($key, 0, 1), $list)) {

                $list[substr($key, 0, 1)][] = $val;
            } else {
                $list[substr($key, 0, 1)][] = $val;
            }

        }

        $allApiS = $list;
        unset($list);

        // 载入接口模板
        $this->load->view('docs/api_list_tpl', array('allApiS' => $allApiS));

    }


    /**
     * ApiDesc - 在线接口描述查看
     * @desc 查看接口详情
     */
    public function service()
    {
        $this->load->library('Request');
        $get = $this->input->get('name');
        $path = explode('/', "/" . $get);
        $actionName = strtolower(array_pop($path)); // 控制器名
        $className = ucfirst(array_pop($path)); // 类名
        $apiDir = implode("/", $path) . "/"; // api路径

        $service = $get;
        $rules = array();
        $returns = array();
        $description = '';
        $descComment = '//请使用@desc 注释';
        $exceptions = array();

        $typeMaps = array(
            'string' => '字符串',
            'int' => '整型',
            'float' => '浮点型',
            'boolean' => '布尔型',
            'date' => '日期',
            'array' => '数组',
            'fixed' => '固定值',
            'enum' => '枚举类型',
            'object' => '对象',
        );

        // 加载接口所在文件
        require_once(__DIR__ . $apiDir . $className . ".php");  //todo classname 替换为路径
        $className .= 'Controller';

        // 整合需要的类注释，包括父类注释
        $rClass = new \ReflectionClass($className);
        $classDocComment = $rClass->getDocComment();
        $needClassDocComment = '';
        foreach (explode("\n", $classDocComment) as $comment) {
            if (stripos($comment, '@exception') !== FALSE
                || stripos($comment, '@return') !== FALSE) {
                $needClassDocComment .= "\n" . $comment;
            }
        }

        // 获取参数列表
        $rules = $this->request->getApiRules($className, $actionName);
        $rules = array_map(function ($rule) {
            $item = [];
            $other = array();
            if (isset($rule['min'])) {
                $other[] = '最小：' . $rule['min'];
            }
            if (isset($rule['max'])) {
                $other[] = '最大：' . $rule['max'];
            }
            if (isset($rule['range'])) {
                $other[] = '范围：' . implode('/', $rule['range']);
            }
            if (isset($rule['source'])) {
                $other[] = '数据源：' . strtoupper($rule['source']);
            }
            if (isset($rule['ext'])) {
                $other[] = '允许格式：' . implode('/', $rule['ext']);
            }
            $item['other'] = implode('；', $other);
            if (!isset($rule['type'])) {
                $rule['type'] = 'string';
            }
            $item['name'] = $rule['name'];
            $item['require'] = isset($rule['require']) ? $rule['require'] : '';
            $item['desc'] = isset($rule['desc']) ? $rule['desc'] : '';
            $item['type'] = isset($typeMaps[$rule['type']]) ? $typeMaps[$rule['type']] : $rule['type'];
            $item['default'] = isset($rule['default']) ? $rule['default'] : '';
            return $item;
        }, $rules);


        // 方法注释
        $rMethod = new \ReflectionMethod($className, $actionName);
        $docCommentArr = explode("\n", $needClassDocComment . "\n" . $rMethod->getDocComment());

        foreach ($docCommentArr as $comment) {
            $comment = trim($comment);

            //标题描述
            if (empty($description) && strpos($comment, '@') === FALSE && strpos($comment, '/') === FALSE) {
                $description = substr($comment, strpos($comment, '*') + 1);
                continue;
            }

            //@desc注释
            $pos = stripos($comment, '@desc');
            if ($pos !== FALSE) {
                $descComment = substr($comment, $pos + 5);
                continue;
            }

            //@exception注释
            $pos = stripos($comment, '@exception');
            if ($pos !== FALSE) {
                $exArr = explode(' ', trim(substr($comment, $pos + 10)));
                $exceptions[$exArr[0]] = $exArr;
                continue;
            }

            //@return注释
            $pos = stripos($comment, '@return');
            if ($pos === FALSE) {
                continue;
            }

            $returnCommentArr = explode(' ', substr($comment, $pos + 8));
            //将数组中的空值过滤掉，同时将需要展示的值返回
            $returnCommentArr = array_values(array_filter($returnCommentArr));
            if (count($returnCommentArr) < 2) {
                continue;
            }
            if (!isset($returnCommentArr[2])) {
                $returnCommentArr[2] = '';    //可选的字段说明
            } else {
                //兼容处理有空格的注释
                $returnCommentArr[2] = implode(' ', array_slice($returnCommentArr, 2));
            }
            $returnCommentArr[0] = isset($typeMaps[$returnCommentArr[0]]) ? $typeMaps[$returnCommentArr[0]] : $returnCommentArr[0];

            //以返回字段为key，保证覆盖
            $returns[$returnCommentArr[1]] = $returnCommentArr;
        }

        $url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) ? 'https://' : 'http://';
        $url = $url . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost');
        $url .= '/api.php';
        $url .= substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1);
        $url .= $service;

        $this->load->view('docs/api_desc_tpl', array(
            'service' => $service,
            'description' => $description,
            'descComment' => $descComment,
            'rules' => $rules,
            'returns' => $returns,
            'exceptions' => $exceptions,
            'url' => $url
        ));
    }


    /**
     * @param $dir
     * @return array
     */
    function __listDir($dir)
    {
        $dir .= substr($dir, -1) == DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR;
        $dirInfo = array();
        foreach (glob($dir . '*') as $v) {
            if (is_dir($v)) {
                $dirInfo = array_merge($dirInfo, $this->__listDir($v));
            } else {
                $dirInfo[] = $v;
            }
        }
        return $dirInfo;
    }

    protected function _auth()
    {
        if (ENVIRONMENT !== 'development') show_404();
    }
}